/*
 *  Copyright (C) 2010 Fabien Renaud
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.boc.botv.service.impl;

import com.boc.botv.common.PaymentMethodStatus;
import com.boc.botv.dao.BetChoiceDao;
import com.boc.botv.dao.BetInstanceDao;
import com.boc.botv.dao.PaymentMethodDao;
import com.boc.botv.dao.SiteAccountDao;
import com.boc.botv.dao.TransactionDao;
import com.boc.botv.dao.UserDao;
import com.boc.botv.model.BetChoice;
import com.boc.botv.model.BetInstance;
import com.boc.botv.model.PaymentMethod;
import com.boc.botv.model.SiteAccount;
import com.boc.botv.model.Transaction;
import com.boc.botv.model.User;
import com.boc.botv.model.UserRememberMe;
import com.boc.botv.service.UserManager;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

/**
 *
 * @author Fabien Renaud
 */
@Service("userManager")
//@Transactional(readOnly = true)
public class UserManagerImpl implements UserManager {

  private final Log logger = LogFactory.getLog(UserManagerImpl.class);
  private UserDao userDao;
  private BetChoiceDao betChoiceDao;
  private BetInstanceDao betInstanceDao;
  private PaymentMethodDao paymentMethodDao;
  private SiteAccountDao siteAccountDao;
  private TransactionDao transactionDao;

  @Autowired
  public void setDao(UserDao userDao, BetChoiceDao betChoiceDao, BetInstanceDao betInstanceDao, PaymentMethodDao paymentMethodDao, SiteAccountDao siteAccountDao, TransactionDao transactionDao) {
    this.userDao = userDao;
    this.betChoiceDao = betChoiceDao;
    this.betInstanceDao = betInstanceDao;
    this.paymentMethodDao = paymentMethodDao;
    this.siteAccountDao = siteAccountDao;
    this.transactionDao = transactionDao;
  }

  public List<User> getUsers() {
    logger.info("[UserManager] Getting all users");
    return userDao.getUsers();
  }

  public boolean loginExists(String login) {
    logger.info("[UserManager] Testing if login `" + login + "` exists");
    return userDao.loginExists(login);
  }

  public boolean mailExists(String mail) {
    logger.info("[UserManager] Testing if mail `" + mail + "` exists");
    return userDao.mailExists(mail);
  }

  public User getUser(int userId) {
    logger.info("[UserManager] Getting user #" + userId);
    return userDao.getUser(userId);
  }

  public User getUser(String login) {
    logger.info("[UserManager] Getting user `" + login + "`");
    return userDao.getUser(login);
  }

  //@Transactional(readOnly = false)
  public void regeneratePassword(String login) {
    logger.info("[UserManager] Regenerating password for `" + login + "`");
    User user = userDao.getUser(login);
    String pwd = generatePassword();
    logger.info("[UserManager] Generated password `" + pwd + "`");
    user.setPassword(encryptPassword(pwd));
    // TODO envoi d'un mail
    user.setLastModification(new Date());
    userDao.updateUser(user);
  }

  //@Transactional(readOnly = false)
  public boolean saveUser(User user) {
    logger.info("[UserManager] Saving user `" + user.getLogin() + "`");
    if (!user.getPassword().equals(user.getCheckPassword())) {
      return false;
    }
    user.setPassword(encryptPassword(user.getPassword()));
    Date now = new Date();
    user.setCreationTime(now);
    user.setLastModification(now);
    user.setLastConnection(now);
    userDao.saveUser(user);

    return true;
  }

  //@Transactional(readOnly = false)
  public boolean updateUser(User user) {
    logger.info("[UserManager] Updating user `" + user.getLogin() + "`");
    user.setLastModification(new Date());
    userDao.updateUser(user);

    return true;
  }

  //@Transactional(readOnly = false)
  public void deleteUser(User user) {
    logger.info("[UserManager] Deleting user `" + user.getLogin() + "`");
    userDao.deleteUser(user);
  }

  //@Transactional(readOnly = false)
  public boolean loqIn(String login, String password) {
    logger.info("[UserManager] Try to log in `" + login + "`");
    User user = userDao.getUser(login);
    if (user == null || !encryptPassword(password).equals(user.getPassword())) {
      return false;
    }
    List<GrantedAuthority> ga = new ArrayList<GrantedAuthority>();
    ga.add(new GrantedAuthorityImpl("ROLE_USER"));
    if (user.getId() == 1) {
      ga.add(new GrantedAuthorityImpl("ROLE_ADMIN"));
    }
    Authentication result = new RememberMeAuthenticationToken(login, new UserRememberMe(user), ga);
    SecurityContextHolder.getContext().setAuthentication(result);
    user.setLastConnection(new Date());

    return true;
  }

  public List<User> getWinners(int betId) {
    List<BetInstance> betInstances = this.getGamesByBetId(betId);
    List<User> winners = new ArrayList<User>();
    for (BetInstance betInstance : betInstances) {
      if (betInstance.getStatus() == 1) {
        User user = userDao.getUser(betInstance.getUser().getId());
        winners.add(user);
      }
    }

    return winners;
  }

  public List<User> getLosers(int betId) {
    List<BetInstance> betInstances = this.getGamesByBetId(betId);
    List<User> losers = new ArrayList<User>();
    for (BetInstance betInstance : betInstances) {
      if (betInstance.getStatus() == 0) {
        User user = userDao.getUser(betInstance.getUser().getId());
        losers.add(user);
      }
    }

    return losers;
  }

  public List<User> getUserWhoChoosed(int choiceId) {
    List<BetInstance> betInstances = betInstanceDao.getGamesByChoice(choiceId);
    List<User> whoChoosed = new ArrayList<User>();
    for (BetInstance betInstance : betInstances) {
      User user = userDao.getUser(betInstance.getUser().getId());
      whoChoosed.add(user);
    }

    return whoChoosed;
  }

  public boolean checkPassword(String login, String password) {
    User user = userDao.getUser(login);
    if (user == null) {
      return false;
    }

    return user.getPassword().equals(encryptPassword(password));
  }

  public boolean addPaymentMethod(int userId, String ownerName, String cardNumber, String cardName, Date cardExpirationDate, String cardCryptogram) {
    if (userId < 2) {
      return false;
    } else if (ownerName.trim().length() < 2) {
      return false;
    } else if (cardName.trim().length() < 2) {
      return false;
    } else if (cardNumber.trim().length() != 16) {
      return false;
    } else if (cardCryptogram.trim().length() != 3) {
      return false;
    }
    User u = userDao.getUser(userId);

    u.increaseBalance(100);
    Authentication a = SecurityContextHolder.getContext().getAuthentication();
    ((UserRememberMe) a.getPrincipal()).setBalance(u.getBalance());

    SiteAccount sa = siteAccountDao.getAccount(4);
    Transaction t = new Transaction(u, sa, "Bonus de création de compte bancaire", new Date(), 0, 100);
    transactionDao.saveTransaction(t);

    PaymentMethod pm = new PaymentMethod(u.getId(), u, new Date(), ownerName, cardNumber, cardExpirationDate, cardName, cardCryptogram, PaymentMethodStatus.disabled);
    paymentMethodDao.savePaymentMethod(pm);
    return true;
  }

  public boolean deletePaymentMethod(int userId) {
    if (userId < 2) {
      return false;
    }

    paymentMethodDao.deletePaymentMethod(userId);
    return true;
  }

  private List<BetInstance> getGamesByBetId(int betId) {
    List<BetChoice> choices = betChoiceDao.getChoices(betId);
    List<BetInstance> list = new ArrayList<BetInstance>();
    if (choices != null) {
      for (BetChoice c : choices) {
        List<BetInstance> l = betInstanceDao.getGamesByChoice(c.getId());
        if (l != null) {
          list.addAll(l);
        }
      }
    }

    return list;
  }

  public String encryptPassword(String password) {
    return DigestUtils.md5Hex(password);
  }

  private String generatePassword() {
    String pwd = "aaaaaaaaaa";
    //TODO
    return pwd;
  }
}
